package com.taysear.parcel.fieldfactory;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;

import com.taysear.parcel.ParcelApplication;
import com.taysear.parcel.PermissionsConstants;
import com.taysear.parcel.domain.Account;
import com.taysear.parcel.domain.Country;
import com.taysear.parcel.domain.Customer;
import com.taysear.parcel.domain.ParcelType;
import com.taysear.parcel.domain.PaymentMode;
import com.taysear.parcel.domain.Station;
import com.taysear.parcel.domain.Substation;
import com.taysear.parcel.util.Constants;
import com.vaadin.addon.jpacontainer.JPAContainer;
import com.vaadin.addon.jpacontainer.JPAContainerFactory;
import com.vaadin.data.Item;
import com.vaadin.data.Property;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.util.filter.And;
import com.vaadin.data.util.filter.Compare.Equal;
import com.vaadin.data.util.filter.Or;
import com.vaadin.data.validator.DoubleValidator;
import com.vaadin.data.validator.IntegerValidator;
import com.vaadin.data.validator.RegexpValidator;
import com.vaadin.data.validator.StringLengthValidator;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.Component;
import com.vaadin.ui.DefaultFieldFactory;
import com.vaadin.ui.Field;
import com.vaadin.ui.Label;
import com.vaadin.ui.NativeSelect;
import com.vaadin.ui.PasswordField;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;

@SuppressWarnings("serial")
public class ParcelFieldFactory extends DefaultFieldFactory {
	private ParcelApplication app;

	public final ComboBox station = new ComboBox();
	public final ComboBox subStation = new ComboBox();
	public final ComboBox destination = new ComboBox();
	public final ComboBox destinationSubstation = new ComboBox();
	public final ComboBox parcelType = new ComboBox();
	public final ComboBox paymentMode = new ComboBox();
	public final TextArea description = new TextArea();
	public TextField quantity = new TextField(); 
	public TextField weight = new TextField();
	public final TextField amount = new TextField();
	public final TextField vatAmount = new TextField();
	public final CheckBox vatable = new CheckBox();
	public final ComboBox account = new ComboBox();
	public final TextField senderName = new TextField();
	public final TextField senderTel = new TextField();
	public final TextField receiverName = new TextField();
	public final TextField receiverTel = new TextField();
	public final TextField senderIdentification = new TextField();
	public final TextField receiverIdentification = new TextField();
	public final TextField verificationCode = new TextField();
	
	public Label stationLabel = new Label("Station");
	public Label subStationLabel = new Label("Sub station");
	public Label accountLabel = new Label("Customer account");
	public Label senderNameLabel = new Label("Sender name");
	public Label senderTelLabel = new Label("Sender tel");
	public Label senderIdentificationLabel = new Label("Sender id details");
	public Label destinationLabel = new Label("Destination");
	public Label destinationSubstationLabel = new Label("Dest sub station");
	public Label receiverNameLabel = new Label("Receiver name");
	public Label receiverTelLabel = new Label("Receiver tel");
	public Label receiverIdentificationLabel = new Label("Receiver id details");
	public Label descriptionLabel = new Label("Description");
	public Label paymentModeLabel = new Label("Payment mode");
	public Label parcelTypeLabel = new Label("Parcel type");
	public Label quantityLabel = new Label("Quantity");
	public Label weightLabel = new Label("Weight");
	public Label amountLabel = null;
	public Label vatableLabel = new Label("Vatable");
	public Label vatAmountLabel = null;
	public Label verificationCodeLabel = new Label("Verification code");

	public ParcelFieldFactory(final ParcelApplication app) {
		this.app = app;
		Subject currentUser = SecurityUtils.getSubject();
		
		Country country = app.em.find(Country.class, app.getUser().getEmployee().getStation().getCountry().getCountryId());
		amountLabel = new Label("Amount ("+country.getCurrency().getName()+")");
		vatAmountLabel = new Label("VAT amount ("+country.getCurrency().getName()+")");
		
		station.setWidth(Constants.COMMON_FIELD_WIDTH);
		station.setNullSelectionAllowed(false);
		station.setItemCaptionPropertyId("name");
		station.setImmediate(true);
		station.setFilteringMode(ComboBox.FILTERINGMODE_STARTSWITH);
		station.setRequired(true);

		subStation.setWidth(Constants.COMMON_FIELD_WIDTH);
		subStation.setItemCaptionPropertyId("name");
		subStation.setNullSelectionAllowed(false);
		subStation.setFilteringMode(ComboBox.FILTERINGMODE_STARTSWITH);
		subStation.setRequired(true);
		
		destination.setWidth(Constants.COMMON_FIELD_WIDTH);
		destination.setItemCaptionPropertyId("name");
		destination.setNullSelectionAllowed(false);
		destination.setFilteringMode(ComboBox.FILTERINGMODE_STARTSWITH);
		destination.setRequired(true);
		destination.setImmediate(true);
		
		destination.addListener(new ValueChangeListener() {
			@Override
			public void valueChange(ValueChangeEvent event) {
				if(destination.getValue() != null){
					JPAContainer<Substation> container = JPAContainerFactory.make(Substation.class, app.PERSISTENCE_UNIT);
					Equal selectedDestStation = new Equal("station.stationId", destination.getValue());
					container.addContainerFilter(selectedDestStation);
					destinationSubstation.setContainerDataSource(container);
				}
			}
		});
		destinationSubstation.setRequired(true);
		destinationSubstation.setNullSelectionAllowed(false);
		destinationSubstation.setWidth(Constants.COMMON_FIELD_WIDTH);
		destinationSubstation.setItemCaptionPropertyId("name");
		destinationSubstation.setFilteringMode(ComboBox.FILTERINGMODE_STARTSWITH);

		parcelType.setWidth(Constants.COMMON_FIELD_WIDTH);
		JPAContainer<ParcelType> p = JPAContainerFactory.make(ParcelType.class, app.PERSISTENCE_UNIT);
		parcelType.setContainerDataSource(p);
		parcelType.setItemCaptionPropertyId("description");
		parcelType.setNullSelectionAllowed(false);
		parcelType.setFilteringMode(ComboBox.FILTERINGMODE_STARTSWITH);
		parcelType.setRequired(true);
		
		paymentMode.setWidth(Constants.COMMON_FIELD_WIDTH);
		JPAContainer<PaymentMode> pay = JPAContainerFactory.make(PaymentMode.class, app.PERSISTENCE_UNIT);
		Equal paymentmodeCash = new Equal("paymentModeId", 1);
		Equal paymentmodeAccount = new Equal("paymentModeId", 2);
		Equal paymentmodeFoc = new Equal("paymentModeId", 3);
		Equal paymentmodeCod = new Equal("paymentModeId", 4);
		Or or = new Or(paymentmodeCash, paymentmodeCod);
		if (currentUser.isPermitted(PermissionsConstants.PERMISSION_PARCEL_OUTGOING_CREATE_PAYMENT_MODE_ACCOUNT))
			or = new Or(paymentmodeCash, paymentmodeAccount);
		if (currentUser.isPermitted(PermissionsConstants.PERMISSION_PARCEL_OUTGOING_CREATE_PAYMENT_MODE_FOC))
			or = new Or(paymentmodeCash, paymentmodeFoc);
		if (currentUser.isPermitted(PermissionsConstants.PERMISSION_PARCEL_OUTGOING_CREATE_PAYMENT_MODE_ACCOUNT) && currentUser.isPermitted(PermissionsConstants.PERMISSION_PARCEL_OUTGOING_CREATE_PAYMENT_MODE_FOC))
			or = new Or(paymentmodeCash, paymentmodeAccount, paymentmodeFoc);
			
		pay.addContainerFilter(or);
		paymentMode.setContainerDataSource(pay);
		paymentMode.setItemCaptionPropertyId("description");
		paymentMode.setNullSelectionAllowed(false);
		paymentMode.setFilteringMode(ComboBox.FILTERINGMODE_STARTSWITH);
		paymentMode.setRequired(true);
		paymentMode.setImmediate(true);
		paymentMode.addListener(new ValueChangeListener() {
			@Override
			public void valueChange(ValueChangeEvent event) {
				if(paymentMode.getValue() != null && (Integer)paymentMode.getValue() == Constants.PARCEL_PAYMENT_MODE_ACCOUNT){
					JPAContainer<Account> container = JPAContainerFactory.make(Account.class, app.PERSISTENCE_UNIT);
					account.setContainerDataSource(container);
					account.setVisible(true);
					account.setRequired(true);
					account.setRequiredError("Please select a account");
					accountLabel.setVisible(true);
				} else {
					account.setContainerDataSource(null);
					account.setVisible(false);
					account.setRequired(false);
					accountLabel.setVisible(false);
				}
			}
		});
		
		account.setWidth(Constants.COMMON_FIELD_WIDTH);
		account.setItemCaptionMode(ComboBox.ITEM_CAPTION_MODE_ITEM);
		account.setNullSelectionAllowed(false);
		account.setFilteringMode(ComboBox.FILTERINGMODE_STARTSWITH);
		account.setImmediate(true);
		account.setImmediate(true);
	}

	@Override
	public Field createField(Item item, Object propertyId, Component uiContext) {
		Field f;
		if ("station".equals(propertyId)) {
			return station;
		} else if ("subStation".equals(propertyId)) {
			return subStation;
		} else if ("destination".equals(propertyId)) {
			destination.setRequiredError("Please select a destination");
			return destination;
		} else if ("destinationSubstation".equals(propertyId)) {
			return destinationSubstation;
		} else if ("parcelType".equals(propertyId)) {
			parcelType.setRequiredError("Please select a parcel type");
			return parcelType;
		} else if ("paymentMode".equals(propertyId)) {
			paymentMode.setRequiredError("Please select a payment mode");
			return paymentMode;
		} else if ("account".equals(propertyId)) {
			return account;
		} else if ("description".equals(propertyId)) {
			description.setNullRepresentation("");
			description.setWidth(Constants.COMMON_FIELD_WIDTH);
			return description;
		} else if ("verificationCode".equals(propertyId)) {
			verificationCode.setNullRepresentation("");
			verificationCode.setWidth(Constants.COMMON_FIELD_WIDTH);
			verificationCode.addValidator(new IntegerValidator("4 digits required"));
			verificationCode.addValidator(new StringLengthValidator("4 digits required", 4, 4, false));
			return verificationCode;
		} else if ("senderName".equals(propertyId)) {
			senderName.setNullRepresentation("");
			senderName.setRequired(true);
			senderName.setRequiredError("Please enter a sender name");
			senderName.setWidth(Constants.COMMON_FIELD_WIDTH);
			senderName.addValidator(new StringLengthValidator(
					"Sender name must be 3-25 characters", 3, 25, false));
			return senderName;
		} else if ("senderTel".equals(propertyId)) {
			senderTel.setNullRepresentation("");
			senderTel.setRequired(true);
			senderTel.setRequiredError("Please enter a sender tel");
			senderTel.setWidth(Constants.COMMON_FIELD_WIDTH);
			senderTel.addValidator(Constants.telValidator);
			senderTel.setImmediate(true);
			return senderTel;
		} else if ("senderIdentification".equals(propertyId)) {
			senderIdentification.setNullRepresentation("");
			senderIdentification.setWidth(Constants.COMMON_FIELD_WIDTH);
			return senderIdentification;
		} else if ("receiverName".equals(propertyId)) {
			receiverName.setNullRepresentation("");
			receiverName.setRequired(true);
			receiverName.setRequiredError("Please enter a receiver name");
			receiverName.setWidth(Constants.COMMON_FIELD_WIDTH);
			receiverName.addValidator(new StringLengthValidator(
					"Receiver name must be 3-25 characters", 3, 25, false));
			return receiverName;
		} else if ("receiverTel".equals(propertyId)) {
			receiverTel.setNullRepresentation("");
			receiverTel.setRequired(true);
			receiverTel.setRequiredError("Please enter a receiver tel");
			receiverTel.setWidth(Constants.COMMON_FIELD_WIDTH);
			receiverTel.addValidator(Constants.telValidator);
			receiverTel.setImmediate(true);
			return receiverTel;
		} else if ("receiverIdentification".equals(propertyId)) {
			receiverIdentification.setNullRepresentation("");
			receiverIdentification.setWidth(Constants.COMMON_FIELD_WIDTH);
			return receiverIdentification;
		} else if ("quantity".equals(propertyId)) {
			quantity.setNullRepresentation("");
			quantity.setRequired(true);
			quantity.addValidator(new IntegerValidator("Quantity must be an Integer"));
			quantity.setWidth(Constants.COMMON_FIELD_WIDTH);
			return quantity;
		} else if ("weight".equals(propertyId)) {
			weight.setNullRepresentation("");
			weight.setRequired(true);
			weight.addValidator(new DoubleValidator("Weight must be an Number"));
			weight.setWidth(Constants.COMMON_FIELD_WIDTH);
			return weight;
		} else if ("amount".equals(propertyId)) {
			amount.setImmediate(true);
			amount.addListener(new VatAmountValueChangeListener());
			amount.setNullRepresentation("");
			amount.setRequired(true);
			amount.addValidator(new DoubleValidator("Amount must be an Number"));
			amount.setWidth(Constants.COMMON_FIELD_WIDTH);
			amount.setImmediate(true);
			return amount;
		} else if ("vatAmount".equals(propertyId)) {
			vatAmount.setNullRepresentation("");
			vatAmount.setEnabled(false);
			vatAmount.setWidth(Constants.COMMON_FIELD_WIDTH);
			return vatAmount;
		} else if ("vatable".equals(propertyId)) {
			vatable.setValue(true);
			vatable.setImmediate(true);
			vatable.setWidth(Constants.COMMON_FIELD_WIDTH);
			vatable.addListener(new VatAmountValueChangeListener());
			return vatable;
		} else {
			// Use the super class to create a suitable field base on the property type.
			f = super.createField(item, propertyId, uiContext);
		}
		return f;
	}

	private PasswordField createPasswordField(Object propertyId) {
		PasswordField pf = new PasswordField();
		pf.setCaption(DefaultFieldFactory.createCaptionByPropertyId(propertyId));
		return pf;
	}
	
	private class VatAmountValueChangeListener implements Property.ValueChangeListener {
		private static final long serialVersionUID = 8926062304101475096L;

		@Override
		public void valueChange(ValueChangeEvent event) {
			if(amount.getValue() != null){
				try{
					if((Boolean) vatable.getValue()){
						double value = Double.parseDouble(amount.getValue().toString());
						Country country = app.em.find(Country.class, app.getUser().getEmployee().getStation().getCountry().getCountryId());
						app.em.refresh(country);
						double vat = value * country.getVat();
						vatAmount.setValue(app.formatDouble(vat));
					}else{
						vatAmount.setValue("");
					}
				}catch(Exception e){
					vatAmount.setValue("");
				}
			}
		}
	}
}
